package com.zzh.myapplication.tools;

import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.widget.ImageView;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.ref.SoftReference;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ImageAsyncLoader {

    private final static int CORE_POOL_SIZE = 2;

    private final static int MAX_POOL_SIZE = 5;

    private final static int KEEP_ALIVE_TIME = 180;

    private static ImageAsyncLoader instance;

    private HashMap<String, SoftReference<BitmapDrawable>> imageCache = new HashMap<String, SoftReference<BitmapDrawable>>();

    @SuppressWarnings("rawtypes")
    private BlockingQueue queue;

    private ThreadPoolExecutor executor;

    private int screenWidth = 0;

    private int screenHeight = 0;

    private String cachePath = "/imagecache";

    @SuppressWarnings({
            "rawtypes", "unchecked"
    })
    private ImageAsyncLoader() {
        queue = new LinkedBlockingQueue();
        executor = new ThreadPoolExecutor(CORE_POOL_SIZE, MAX_POOL_SIZE,
                KEEP_ALIVE_TIME, TimeUnit.SECONDS, queue,
                new ThreadPoolExecutor.CallerRunsPolicy());
    }

    public static ImageAsyncLoader getInstance() {
        if (instance == null) {
            instance = new ImageAsyncLoader();
        }
        return instance;
    }

    public String getCachePath() {
        return cachePath;
    }

    public void setCachePath(String cachePath) {
        this.cachePath = cachePath;
    }

    /**
     * description:�����Դid��ȡ����ͼƬ<br/>
     * modify note:<br/>
     * author:Ryan.Van<br/>
     * date:2015-2-4<br/>
     * 
     * @param res ��Դ����
     * @param resId ��Դid
     * @return ͼƬ����
     */
    public BitmapDrawable getBitmapDrawable(Resources res, int resId) {
        BitmapDrawable bitmapDrawable = null;
        SoftReference<BitmapDrawable> softReference = null;
        String strRes = String.valueOf(resId);
        if (imageCache.containsKey(strRes)) {
            softReference = imageCache.get(strRes);
            if (softReference != null) {
                bitmapDrawable = softReference.get();
                if (bitmapDrawable != null) {
                    return bitmapDrawable;
                }
            }
        }
        InputStream inputStream = res.openRawResource(resId);
        bitmapDrawable = new BitmapDrawable(res, inputStream);
        imageCache.put(strRes,
                new SoftReference<BitmapDrawable>(bitmapDrawable));
        return bitmapDrawable;
    }

    /**
     * description:����ͼƬ���������ڣ������ز����棻�����������<br/>
     * modify note:<br/>
     * author:Ryan.Van<br/>
     * date:2015-2-4<br/>
     * 
     * @param imageUrl
     * @param imageView
     * @param imageCallback
     * @param isAvatar
     * @param screenW
     * @param screenH
     * @param res
     * @return
     */
    public BitmapDrawable loadBitmapDrawable(final String imageUrl,
            final ImageView imageView, final ImageCallback imageCallback,
            final boolean isAvatar, int screenW, int screenH, final Resources res) {
        final String key = Encrypt.get32MD5Str(imageUrl);
        this.screenHeight = screenH;
        this.screenWidth = screenW;
        BitmapDrawable bitmapDrawable = null;
        SoftReference<BitmapDrawable> softReference = null;
        final Handler handler = new Handler(new Handler.Callback() {
            public boolean handleMessage(Message msg) {
                imageCallback.imageLoaded((BitmapDrawable) msg.obj, imageView,
                        imageUrl);
                return false;
            };
        });
        if (imageCache.containsKey(key)) {
            softReference = imageCache.get(key);
            if (softReference != null) {
                bitmapDrawable = softReference.get();
                if (bitmapDrawable != null) {
                    return bitmapDrawable;
                }
            }
        }
        if (isBitmapExist(key)) {
            bitmapDrawable = getBitmapDrawableFromCache(res, key);
            imageCache.put(key, new SoftReference<BitmapDrawable>(bitmapDrawable));
            return bitmapDrawable;
        }
        executor.execute(new Runnable() {

            @Override
            public void run() {
                boolean status = getHttpBitmapDrawable(imageUrl);
                if (status) {
                    BitmapDrawable temp = getBitmapDrawableFromCache(res, key);
                    Message message = handler.obtainMessage(0, temp);
                    handler.sendMessage(message);
                } else {
                    File file = new File(String.format("%s%s%s",
                            isCacheFileIsExist(), File.separator, key));
                    deleteFile(file);
                }
            }
        });
        return null;
    }

    /**
     * 
     * description:�ӻ����л�ȡͼƬ���<br/>
     * modify note:<br/>
     * author:Ryan.Van<br/>
     * date:2015-2-4<br/>
     * @param res
     * @param key
     * @return
     */
    private BitmapDrawable getBitmapDrawableFromCache(Resources res, String key) {
        BitmapDrawable bitmapDrawable = null;
        String filePath = isCacheFileIsExist();
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        Bitmap bitmap = BitmapFactory.decodeFile(
                String.format("%s%s%s", filePath, File.separator, key), options);
        options.inSampleSize = computSampleSize(options, -1, screenHeight * screenWidth);
        options.inPreferredConfig = Bitmap.Config.RGB_565;
        options.inPurgeable = true;
        options.inInputShareable = true;
        options.inJustDecodeBounds = false;
        bitmap = BitmapFactory.decodeFile(String.format("%s%s%s", filePath, File.separator, key),
                options);
        bitmapDrawable = new BitmapDrawable(res, bitmap);
        return bitmapDrawable;
    }

    /**
     * description:�ж�Ŀ¼�Ƿ���ڣ��������򴴽�<br/>
     * modify note:<br/>
     * author:Ryan.Van<br/>
     * date:2015-2-3<br/>
     * 
     * @return Ŀ¼·��
     */
    private String isCacheFileIsExist() {
        String filePath = "";
        String rootPath = "";
        if (Environment.MEDIA_MOUNTED.equalsIgnoreCase(Environment
                .getExternalStorageState())) {
            rootPath = Environment.getExternalStorageDirectory().toString();
        }
        filePath = rootPath + cachePath;
        File file = new File(filePath);
        if (!file.exists()) {
            file.mkdirs();
        }
        return filePath;
    }

    /**
     * description:���ز�����ͼƬ<br/>
     * modify note:<br/>
     * author:Ryan.Van<br/>
     * date:2015-2-3<br/>
     * 
     * @param url ͼƬ���ص�ַ
     * @return true,���ز�����ɹ�;false,����ʧ��
     */
    public boolean getHttpBitmapDrawable(String url) {
        boolean result = false;
        URL httpUrl = null;
        InputStream is = null;
        try {
            httpUrl = new URL(url);
            HttpURLConnection connection = (HttpURLConnection) httpUrl.openConnection();
            is = (InputStream) connection.getInputStream();
            if (is != null) {
                writeToSdFromInput(is, url);
                result = true;
            }
            is.close();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * description:��ͼƬ������SD����<br/>
     * modify note:<br/>
     * author:Ryan.Van<br/>
     * date:2015-2-3<br/>
     * 
     * @param inputStream �����ͼƬ��
     * @param name ���������
     * @throws Exception �쳣
     */
    public void writeToSdFromInput(InputStream inputStream, String name) throws Exception {
        String path = String.format("%s%s%s", isCacheFileIsExist(),
                File.separator, Encrypt.get32MD5Str(name));
        OutputStream outputStream = new FileOutputStream(path);
        byte[] buffer = new byte[4 * 1024];
        int length;
        while ((length = inputStream.read(buffer)) != -1) {
            outputStream.write(buffer, 0, length);
        }
        outputStream.flush();
        outputStream.close();
    }

    public static int computSampleSize(BitmapFactory.Options options,
            int minSideLength, int maxNumOfPixels) {
        int initialSize = computeInitialSampleSize(options, minSideLength,
                maxNumOfPixels);
        int roundedSize;
        if (initialSize <= 8) {
            roundedSize = 1;
            while (roundedSize < initialSize) {
                roundedSize <<= 1;
            }
        } else {
            roundedSize = (initialSize + 7) / 8 * 8;
        }
        return roundedSize;
    }

    private static int computeInitialSampleSize(BitmapFactory.Options options,
            int minSideLength, int maxNumOfPixels) {
        double w = options.outWidth;
        double h = options.outHeight;

        int lowerBound = (maxNumOfPixels == -1) ? 1 : (int) Math.ceil(Math
                .sqrt(w * h / maxNumOfPixels));
        int upperBound = (minSideLength == -1) ? 128 : (int) Math.min(
                Math.floor(w / minSideLength), Math.floor(h / minSideLength));

        if (upperBound < lowerBound) {
            // return the larger one when there is no overlapping zone.
            return lowerBound;
        }

        if ((maxNumOfPixels == -1) && (minSideLength == -1)) {
            return 1;
        } else if (minSideLength == -1) {
            return lowerBound;
        } else {
            return upperBound;
        }
    }

    public void clearCache() {
        imageCache.clear();
    }

    private boolean isBitmapExist(String url) {
        return isLocalHasBmp(url);
    }

    private boolean isLocalHasBmp(String url) {
        String filePath = isCacheFileIsExist();
        File file = new File(filePath, url);
        return file.exists();
    }

    public boolean deleteFile(File file) {
        boolean result = false;
        if (file.exists()) {
            if (file.isFile()) {
                file.delete();
                result = true;
            } else if (file.isDirectory()) {
                File files[] = file.listFiles();
                for (int i = 0; i < files.length; i++) {
                    this.deleteFile(files[i]);
                    result = false;
                }
                result = true;
            }
            file.delete();
        } else {
            result = false;
        }
        return result;
    }

    public interface ImageCallback {
        public void imageLoaded(BitmapDrawable imageDrawable,
                                ImageView imageView, String imageUrl);
    }

}
